package com.atguigu.aspect;

import com.atguigu.annotations.MyRedisCache;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.lang.reflect.Parameter;

@Aspect
@Component
@Slf4j
public class MyRedisCacheAspect {
    @Autowired
    private RedisTemplate redisTemplate;

    @Pointcut("@annotation(com.atguigu.annotations.MyRedisCache)")//缓存切点
    public void cachePointCut(){}

    @Around("cachePointCut()")
    public Object doCache(ProceedingJoinPoint joinPoint){
        Object result = null;
        try {
            log.info("进入缓存切面");
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();//获取方法签名
        Method method = signature.getMethod();//获取方法
        MyRedisCache cache = method.getAnnotation(MyRedisCache.class);//获取方法上的注解
        String keyPrefix = cache.keyPrefix();//获取注解的keyPrefix属性
        String matchValue = cache.matchValue();//获取注解的matchValue属性
            System.out.println("matchValue:"+matchValue);
        //SpringEl解析器
        SpelExpressionParser spelExpressionParser = new SpelExpressionParser();//创建Spel表达式解析器
        Expression expression = spelExpressionParser.parseExpression(matchValue);//解析matchValue属性的值
        EvaluationContext Context = new StandardEvaluationContext();//创建标准评估上下文

        Object[] args = joinPoint.getArgs();//获取方法参数值,按照顺序写在数组里
        Parameter[] parameters = method.getParameters();//获取方法参数元数据（名称、类型、注解等) .length表示的是参数个数
        for (int i = 0; i < parameters.length; i++) {
            System.out.println("获得方法里的参数名和值:"+parameters[i].getName()+":"+args[i].toString());
            Context.setVariable(parameters[i].getName(), args[i].toString());//设置参数
        }
        String key = keyPrefix+":"+expression.getValue(Context).toString();//拼接key
        result = redisTemplate.opsForValue().get(key);//通过key查redis
        if (result != null){
            return result;
        }
            result = joinPoint.proceed();//获取方法返回值
            if (result != null){
                redisTemplate.opsForValue().set(key, result);//缓存补偿
            }
        } catch (Throwable e) {
           e.printStackTrace();
        }
        return result;
    }
}
